﻿Imports System.Net
Imports System.Text
Imports System.Web
Imports System.IO
Imports System.Xml
Imports System.Runtime.Serialization

Partial Class TwitterClient
    ''' <summary>
    ''' Twitterのユーザーに関する処理中のデータを保持します。
    ''' </summary>
    ''' <remarks></remarks>
    Private Class FriendshipBundle
        Private _result As Boolean
        Public Property Result() As Boolean
            Get
                Return _result
            End Get
            Set(ByVal value As Boolean)
                _result = value
            End Set
        End Property

        Private _userID As Long
        Public Property UserID() As Long
            Get
                Return _userID
            End Get
            Set(ByVal value As Long)
                _userID = value
            End Set
        End Property

        Private _userName As String
        Public Property UserName() As String
            Get
                Return _userName
            End Get
            Set(ByVal value As String)
                _userName = value
            End Set
        End Property
    End Class

    ''' <summary>
    ''' Twitterのユーザーページから処理に必要な情報を取得します。
    ''' </summary>
    ''' <param name="userName">ユーザー名</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function GetFriendshipBundle(ByVal userName As String) As FriendshipBundle
        Dim bundle As New FriendshipBundle()
        bundle.UserName = userName
        Dim url As String = String.Format("{0}/{1}", My.Resources.TwitterUrl, userName)
        Dim req As HttpWebRequest = DirectCast(WebRequest.Create(url), HttpWebRequest)
        req.SetTwitterGetHeaders(Nothing, _cookies)
        Using res As HttpWebResponse = DirectCast(req.GetResponse(), HttpWebResponse)
            Select Case res.StatusCode
                Case HttpStatusCode.OK
                    res.LoadCookies(_cookies)
                    Dim xmlRes As XmlDocument = res.GetResponseAsXHtml()
                    Dim xnm As XmlNamespaceManager = xmlRes.GetXHtmlNameSpaceManager("x")
                    bundle.UserID = CLng(xmlRes.SelectSingleNode("//x:div[starts-with(@id,'follow_actions_')]", xnm).Attributes("id").Value.Substring(15))
                    bundle.Result = True
                Case Else
                    bundle.Result = False
            End Select
        End Using
        Return bundle
    End Function

    ''' <summary>
    ''' ユーザーをフォローした結果として返されるJSONを格納します。
    ''' </summary>
    ''' <remarks></remarks>
    <DataContract()> _
    Private Class CreateFriendshipJsonResult
        Private _result As String
        <DataMember(Name:="result")> _
        Public Property Result() As String
            Get
                Return _result
            End Get
            Set(ByVal value As String)
                _result = value
            End Set
        End Property
        Private _notice As String
        <DataMember(Name:="notice")> _
        Public Property Notice() As String
            Get
                Return _notice
            End Get
            Set(ByVal value As String)
                _notice = value
            End Set
        End Property
        Private _success As Boolean
        <DataMember(Name:="success")> _
        Public Property Success() As Boolean
            Get
                Return _success
            End Get
            Set(ByVal value As Boolean)
                _success = value
            End Set
        End Property
    End Class

    ''' <summary>
    ''' ユーザー名を指定してユーザーをフォローします。
    ''' </summary>
    ''' <param name="userName">フォローするユーザー名</param>
    ''' <returns>フォローに成功すればTrue、失敗すればFalse</returns>
    ''' <remarks></remarks>
    Public Function CreateFriendship(ByVal userName As String) As Boolean
        If Not IsLogined Then
            Throw New InvalidOperationException(StrThisInstanceIsNotLoginedYet)
        End If

        Dim bundle As FriendshipBundle = GetFriendshipBundle(userName)
        If (bundle.Result = True) Then
            CreateFriendShipPost(bundle)
        End If
        Return bundle.Result
    End Function

    ''' <summary>
    ''' TwitterUserを指定してユーザーをフォローします。
    ''' </summary>
    ''' <param name="user">フォローするTwitterUser</param>
    ''' <returns>フォローに成功すればTrue、失敗すればFalse</returns>
    ''' <remarks></remarks>
    Public Function CreateFriendship(ByVal user As TwitterUser) As Boolean
        If Not IsLogined Then
            Throw New InvalidOperationException(StrThisInstanceIsNotLoginedYet)
        End If

        Dim bundle As New FriendshipBundle()
        bundle.UserID = user.UserID
        bundle.UserName = user.UserName
        CreateFriendShipPost(bundle)
        Return bundle.Result
    End Function

    ''' <summary>
    ''' Twitterにフォローのリクエストをポストして結果を取得します。
    ''' </summary>
    ''' <param name="bundle">処理中のデータを保持するFriendshipBundle</param>
    ''' <remarks></remarks>
    Private Sub CreateFriendShipPost(ByVal bundle As FriendshipBundle)
        Dim url As String = String.Format("{0}{1}{2}", My.Resources.TwitterUrl, My.Resources.TwitterCreateFriendshipPath, bundle.UserID)
        Dim req As HttpWebRequest = DirectCast(WebRequest.Create(url), HttpWebRequest)
        req.SetTwitterXHRPostHeaders(String.Format("{0}/{1}", My.Resources.TwitterUrl, bundle.UserName), _cookies)
        Dim sbPost As New StringBuilder()
        sbPost.AppendFormat("{0}={1}&", HttpUtility.UrlEncode(_authenticityTokenFiledName), HttpUtility.UrlEncode(_authenticityToken))
        sbPost.AppendFormat("{0}={1}", "twttr", "true")
        Dim strPost As String = sbPost.ToString()
        req.PostString(strPost)
        Using res As HttpWebResponse = DirectCast(req.GetResponse(), HttpWebResponse)
            Select Case res.StatusCode
                Case HttpStatusCode.OK
                    res.LoadCookies(_cookies)
                    Dim jsonRsult As CreateFriendshipJsonResult = res.GetResponseAsJson(Of CreateFriendshipJsonResult)()
                    If (jsonRsult.Success) Then
                        bundle.Result = True
                    Else
                        bundle.Result = False
                    End If
                Case Else
                    bundle.Result = False
            End Select
        End Using
    End Sub

    ''' <summary>
    ''' ユーザー名を指定してフォロー解除します。
    ''' </summary>
    ''' <param name="userName">フォロー解除するユーザー名</param>
    ''' <returns>フォロー解除に成功すればTrue、失敗すればFalse</returns>
    ''' <remarks></remarks>
    Public Function DestroyFriendship(ByVal userName As String) As Boolean
        If Not IsLogined Then
            Throw New InvalidOperationException(StrThisInstanceIsNotLoginedYet)
        End If

        Dim bundle As FriendshipBundle = GetFriendshipBundle(userName)
        If (bundle.Result = True) Then
            DestroyFriendShipPost(bundle)
        End If
        Return bundle.Result
    End Function

    ''' <summary>
    ''' TwitterUserを指定してユーザーをフォロー解除します。
    ''' </summary>
    ''' <param name="user">フォロー解除するTwitterUser</param>
    ''' <returns>フォロー解除に成功すればTrue、失敗すればFalse</returns>
    ''' <remarks></remarks>
    Public Function DestroyFriendship(ByVal user As TwitterUser) As Boolean
        If Not IsLogined Then
            Throw New InvalidOperationException(StrThisInstanceIsNotLoginedYet)
        End If

        Dim bundle As New FriendshipBundle()
        bundle.UserID = user.UserID
        bundle.UserName = user.UserName
        DestroyFriendShipPost(bundle)
        Return bundle.Result
    End Function

    ''' <summary>
    ''' Twitterにフォロー解除のリクエストをポストして結果を取得します。
    ''' </summary>
    ''' <param name="bundle"></param>
    ''' <remarks></remarks>
    Private Sub DestroyFriendShipPost(ByVal bundle As FriendshipBundle)
        Dim url As String = String.Format("{0}{1}{2}", My.Resources.TwitterUrl, My.Resources.TwitterDestroyFriendshipPath, bundle.UserID)
        Dim req As HttpWebRequest = DirectCast(WebRequest.Create(url), HttpWebRequest)
        req.SetTwitterXHRPostHeaders(String.Format("{0}/{1}", My.Resources.TwitterUrl, bundle.UserName), _cookies)
        Dim sbPost As New StringBuilder()
        sbPost.AppendFormat("{0}={1}&", HttpUtility.UrlEncode(_authenticityTokenFiledName), HttpUtility.UrlEncode(_authenticityToken))
        sbPost.AppendFormat("{0}={1}", "twttr", "true")
        Dim strPost As String = sbPost.ToString()
        req.PostString(strPost)
        Using res As HttpWebResponse = DirectCast(req.GetResponse(), HttpWebResponse)
            Select Case res.StatusCode
                Case HttpStatusCode.OK
                    res.LoadCookies(_cookies)
                    Using srReq As New StreamReader(res.GetResponseStream())
                        Select Case srReq.ReadToEnd()
                            Case "success"
                                bundle.Result = True
                            Case Else
                                bundle.Result = False
                        End Select
                    End Using
                Case Else
                    bundle.Result = False
            End Select
        End Using
    End Sub

    ''' <summary>
    ''' ユーザー名を指定してブロックします。
    ''' </summary>
    ''' <param name="userName">ブロックするユーザー名</param>
    ''' <returns>ブロックに成功すればTrue、失敗すればFalse</returns>
    ''' <remarks></remarks>
    Public Function CreateBlock(ByVal userName As String) As Boolean
        If Not IsLogined Then
            Throw New InvalidOperationException(StrThisInstanceIsNotLoginedYet)
        End If

        Dim bundle As FriendshipBundle = GetFriendshipBundle(userName)
        If (bundle.Result = True) Then
            CreateBlockPost(bundle)
        End If
        Return bundle.Result
    End Function

    ''' <summary>
    ''' TwitterUserを指定してユーザーをブロックします。
    ''' </summary>
    ''' <param name="user">ブロックするTwitterUser</param>
    ''' <returns>ブロックに成功すればTrue、失敗すればFalse</returns>
    ''' <remarks></remarks>
    Public Function CreateBlock(ByVal user As TwitterUser) As Boolean
        If Not IsLogined Then
            Throw New InvalidOperationException(StrThisInstanceIsNotLoginedYet)
        End If

        Dim bundle As New FriendshipBundle()
        bundle.UserID = user.UserID
        bundle.UserName = user.UserName
        CreateBlockPost(bundle)
        Return bundle.Result
    End Function

    ''' <summary>
    ''' Twitterにブロックのリクエストをポストして結果を取得します。
    ''' </summary>
    ''' <param name="bundle"></param>
    ''' <remarks></remarks>
    Private Sub CreateBlockPost(ByVal bundle As FriendshipBundle)
        Dim url As String = String.Format("{0}{1}{2}", My.Resources.TwitterUrl, My.Resources.TwitterCreateBlockPath, bundle.UserID)
        Dim req As HttpWebRequest = DirectCast(WebRequest.Create(url), HttpWebRequest)
        req.SetTwitterPostHeaders(String.Format("{0}{1}{2}", My.Resources.TwitterUrl, My.Resources.TwitterConfirmBlockPath, bundle.UserID), _cookies)
        Dim sbPost As New StringBuilder()
        sbPost.AppendFormat("{0}={1}&", HttpUtility.UrlEncode(_authenticityTokenFiledName), HttpUtility.UrlEncode(_authenticityToken))
        sbPost.AppendFormat("{0}={1}", "commit", HttpUtility.UrlEncode("はい、このユーザーをブロックします"))
        Dim strPost As String = sbPost.ToString()
        req.PostString(strPost)
        Using res As HttpWebResponse = DirectCast(req.GetResponse(), HttpWebResponse)
            Select Case res.StatusCode
                Case HttpStatusCode.Found
                    res.LoadCookies(_cookies)
                    bundle.Result = True
                Case Else
                    bundle.Result = False
            End Select
        End Using
    End Sub

    ''' <summary>
    ''' ユーザー名を指定してブロック解除します。
    ''' </summary>
    ''' <param name="userName">ブロック解除するユーザー名</param>
    ''' <returns>ブロック解除に成功すればTrue、失敗すればFalse</returns>
    ''' <remarks></remarks>
    Public Function DestroyBlock(ByVal userName As String) As Boolean
        If Not IsLogined Then
            Throw New InvalidOperationException(StrThisInstanceIsNotLoginedYet)
        End If

        Dim bundle As FriendshipBundle = GetFriendshipBundle(userName)
        If (bundle.Result = True) Then
            DestroyBlockPost(bundle)
        End If
        Return bundle.Result
    End Function

    ''' <summary>
    ''' TwitterUserを指定してユーザーをブロック解除します。
    ''' </summary>
    ''' <param name="user">ブロック解除するTwitterUser</param>
    ''' <returns>ブロック解除に成功すればTrue、失敗すればFalse</returns>
    ''' <remarks></remarks>
    Public Function DestroyBlock(ByVal user As TwitterUser) As Boolean
        If Not IsLogined Then
            Throw New InvalidOperationException(StrThisInstanceIsNotLoginedYet)
        End If

        Dim bundle As New FriendshipBundle()
        bundle.UserID = user.UserID
        bundle.UserName = user.UserName
        DestroyBlockPost(bundle)
        Return bundle.Result
    End Function

    ''' <summary>
    ''' Twitterにブロック解除のリクエストをポストして結果を取得します。
    ''' </summary>
    ''' <param name="bundle"></param>
    ''' <remarks></remarks>
    Private Sub DestroyBlockPost(ByVal bundle As FriendshipBundle)
        Dim url As String = String.Format("{0}{1}{2}", My.Resources.TwitterUrl, My.Resources.TwitterDestroyBlockPath, bundle.UserID)
        Dim req As HttpWebRequest = DirectCast(WebRequest.Create(url), HttpWebRequest)
        req.SetTwitterPostHeaders(String.Format("{0}/{1}", My.Resources.TwitterUrl, bundle.UserName), _cookies)
        Dim sbPost As New StringBuilder()
        sbPost.AppendFormat("{0}={1}", HttpUtility.UrlEncode(_authenticityTokenFiledName), HttpUtility.UrlEncode(_authenticityToken))
        Dim strPost As String = sbPost.ToString()
        req.PostString(strPost)
        Using res As HttpWebResponse = DirectCast(req.GetResponse(), HttpWebResponse)
            Select Case res.StatusCode
                Case HttpStatusCode.Found
                    res.LoadCookies(_cookies)
                    bundle.Result = True
                Case Else
                    bundle.Result = False
            End Select
        End Using
    End Sub
End Class
